﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Data;
using System.ComponentModel;
using System.Xml.Linq;
using System.Reflection;

namespace Objects
{
    public class ValueInjector : Valuehost
    {
        internal static Dictionary<OrderGroup<Type, Type>, MethodInfo> op_Convert =
            new Dictionary<OrderGroup<Type, Type>, MethodInfo>();
        internal static Dictionary<OrderGroup<Type, Type>, bool> op_Convert_Incedents =
            new Dictionary<OrderGroup<Type, Type>, bool>();

        public bool TryCast<T>(out T val)
        {
            return TryCast<T>(Value, out val);
        }

        public static bool TryCast<T>(object obj, out T val)
        {
            bool res = true;
            val = default(T);
            try
            {
                val = (T)obj;
            }
            catch
            {
                OrderGroup<Type, Type> asc = new OrderGroup<Type, Type>(typeof(T), obj.GetType());
                MethodInfo mi;
                if (!op_Convert_Incedents.TryGetValue(asc, out res))
                {
                    foreach (var item in asc.Element1.GetMethods().Where(s => s.Name == "op_Explicit" && s.IsSpecialName && s.IsStatic && !op_Convert.ContainsValue(s)))
                        op_Convert.Add(new OrderGroup<Type, Type>(item.ReturnType, item.GetParameters()[0].ParameterType), item);
                    foreach (var item in asc.Element1.GetMethods().Where(s => s.Name == "op_Implicit" && s.IsSpecialName && s.IsStatic && !op_Convert.ContainsValue(s)))
                        op_Convert.Add(new OrderGroup<Type, Type>(item.ReturnType, item.GetParameters()[0].ParameterType), item);

                    foreach (var item in asc.Element2.GetMethods().Where(s => s.Name == "op_Explicit" && s.IsSpecialName && s.IsStatic && !op_Convert.ContainsValue(s)))
                        op_Convert.Add(new OrderGroup<Type, Type>(item.ReturnType, item.GetParameters()[0].ParameterType), item);
                    foreach (var item in asc.Element2.GetMethods().Where(s => s.Name == "op_Implicit" && s.IsSpecialName && s.IsStatic && !op_Convert.ContainsValue(s)))
                        op_Convert.Add(new OrderGroup<Type, Type>(item.ReturnType, item.GetParameters()[0].ParameterType), item);
                    if ((res = op_Convert.TryGetValue(asc, out mi)))
                        val = (T)mi.Invoke(null, new object[] { obj });
                    op_Convert_Incedents.Add(asc, res);
                }
                else if (res)
                {
                    mi = op_Convert[asc];
                    val = (T)mi.Invoke(null, new object[] { obj });
                }
            }
            return res;
        }
        private object _Value;
        public override object Value
        {
            get
            {
                return _Value;
            }
            set
            {
                _Value = value;
            }
        }
        public ValueInjector(object Value)
            : this()
        {
            this.Value = Value;
        }
        protected ValueInjector() { }
        protected override void Clone(ref Valuehost chain)
        {
            ValueInjector res;
            chain = res = (chain as ValueInjector) ?? new ValueInjector();
            res.Value = Value;
        }
    }

    public class LazyObject<T>: ICloneable
    {
        public bool IsPermanent;
        Func<T> func;
        public LazyObject(Func<T> func)
        {
            this.func = func;
        }
        public T GetObject()
        {
            if (func != null)
                return func();
            else
                return default(T);
        }
        public LazyObject()
        {
            IsPermanent = true;
        }

        #region ICloneable Members

        public object Clone()
        {
            return new LazyObject<T>(func) { IsPermanent = IsPermanent };
        }

        #endregion
    }

    public class IndexValInj<T> : ValueInjector
    {
        public T PropertyName;
        public IndexValInj(object Value, T PropertyName)
            : base(Value)
        {
            this.PropertyName = PropertyName;
        }

        protected IndexValInj()
            : base()
        { }

        protected override void Clone(ref Valuehost chain)
        {
            IndexValInj<T> res;
            chain = res = (chain as IndexValInj<T>) ?? new IndexValInj<T>();
            base.Clone(ref chain);
            res.PropertyName = this.PropertyName;
        }
    }

    public class IndexParseInj<T> : ParseInjector
    {
        public T PropertyName;
        public IndexParseInj(string Value, T PropertyName)
            : base(Value)
        {
            this.PropertyName = PropertyName;
        }

        protected IndexParseInj()
            : base()
        { }

        protected override void Clone(ref Valuehost chain)
        {
            IndexParseInj<T> res;
            chain = res = (chain as IndexParseInj<T>) ?? new IndexParseInj<T>();
            base.Clone(ref chain);
            res.PropertyName = this.PropertyName;
        }
    }
}
